package Chat.System;

import Chat.Donnees.*;
import java.io.File;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Ndanja & Papa Abdou
 */
/**
 *  Le Controller dans le modèle MVC
 */
public class ChatController {

    private ChatSystem chatSystem;
    private Contact lUser;
    private ListContacts lContacts;
    private ChatGUI mChatGUI;
    private ChatNI mChatNI;
    private TransfertFile transfert;
    private HashMap listeTransfert;
    private Vector portUtilise, portUnitilise;
    private int tailleFTransfere;
    private int tailleMax;

    public ChatController() {

        lContacts = new ListContacts();
        lUser = new Contact(null, null);
        listeTransfert = new HashMap();
        portUnitilise = new Vector();

        for (int i = 5000; i < 5010; i++) {
            portUnitilise.add(i);
        }
        portUtilise = new Vector();

    }

    /**
     *  Pour creer le messsage pour un Hello
     * @param s: String: le nickname de l'utilisateur local
     * 
     */
    public void performConnect(String s) {

        lUser.setNickname(s);
        lUser.setAddress(this.getLocalAddress());

        mChatNI.sendHello(lUser, Boolean.TRUE, "255.255.255.255");

    }

    /**
     *Permet d'enregistrer un Contact à la réception d'un Hello <br/>
     *1/ n'enregistre pas si s'est un Hello qu'on a envoyé <br/>
     *2/ n'enregistre pas si le Contact est déja enregistré <br/>
     *3/ envoie ou pas un Reply selon la valeur du respReq <br/>
     * @param rContact : Contact source
     * @param respReq: Booelan: si TRUE: envoie Hello si False: juste enregistrement du Contact
     * @throws Exception
     */
    public void newContact(Contact rContact, Boolean respReq) throws Exception {

        Contact contact = lContacts.getContact(rContact.getNickname());

        if (contact != null) {

            String nicknameSave = lContacts.getContact(rContact.getNickname()).getNickname();
            String adresSave = lContacts.getContactByAddress(rContact.getAddress()).getAddress();

            if (!rContact.getNickname().equalsIgnoreCase(nicknameSave) &&
                    !rContact.getAddress().equalsIgnoreCase(adresSave)) {

                lContacts.addContact(rContact.getNickname(), rContact);
                mChatGUI.addContact(rContact.getNickname());

                if (respReq.booleanValue() == Boolean.TRUE) {

                    mChatNI.sendHello(lUser, false, rContact.getAddress());

                }
            }
        } else {

            lContacts.addContact(rContact.getNickname(), rContact);
            mChatGUI.addContact(rContact.getNickname());
            if (respReq.booleanValue() == Boolean.TRUE) {
                mChatNI.sendHello(lUser, false, rContact.getAddress());
            }
        }

    }

    /**
     *  Permet d'envoyer un message pour un seul destinataire
     * @param m
     * @param iContact
     * 
     */
    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.B0BA042B-3490-1FA0-1D39-A556245F5127]
    // </editor-fold> 
//    public void perforSendMsg (String m, ArrayList<Integer> iContact) {
    public void perforSendMsg(String m, Contact iContact) {

        ArrayList listeDestinataire = new ArrayList();
        listeDestinataire.add(iContact);
        mChatNI.sendMsg(m, listeDestinataire, lUser);

    }

    /**
     * Permet l'envoie d'un Bye pendant la deconnexion
     */
    public void performDisconnect() {
        mChatNI.sendBye(lUser);
    }

    /**
     * PerFormSendFile
     * Permet de gérer l'envoie d'un fichier
     *      - ouverture d'un port non utilisé<br/>
     *      - lancement d'un nouveau tranfert de fichier<br/>
     *      - creation du ProgressBar dans le GUI<br/>
     * @param fpath: String: arborescence du fichier
     * @param fname: String nom du Fichier
     * @param iContact: Contact: Contact du destinataire
     */
    //public void performSendFile(String fname, ArrayList<Integer> iContact) {
    public void performSendFile(String fpath, String fname, Contact iContact) {


        Integer port = (Integer) portUnitilise.firstElement();
        portUtilise.add(port);
        portUnitilise.remove(port);

        ArrayList listeDestinataire = new ArrayList();
        listeDestinataire.add(iContact);


        File fileToSend = new File(fpath);
        mChatNI.sendFile(fname, listeDestinataire, lUser, port, (int) fileToSend.length());

        transfert = new TransfertFile(port, fileToSend);
        transfert.setChatController(this);
        transfert.setNom(port);
        mChatGUI.addProgress(transfert.getProgress(), lUser.getNickname(), iContact.getNickname(), fname);
        listeTransfert.put(port, transfert);


    }

    /**
     * Permet de traiter la réception d'un Ack
     *      - si le ack est à false: on supprime le transfert<br/>
     *      - si le ack est à True: on continue le transfert<br/>
     * @param acquitement
     */
    public void performSendFileAck(FichierAck acquitement) {

        transfert = (TransfertFile) listeTransfert.get(acquitement.getPort());
        if (acquitement.getAck() != Boolean.TRUE) {

            transfert = null;
        }

    }

    /**
     * Permet de supprimer un objet de type transfert de fichier<br/>
     *      - permet de libérer un port unitilisé
     * @param t: l'identifiant du TransfertFile
     * @param port: le numéro de port
     */
    void removeTransfert(TransfertFile t, int port) {
        t = null;
        portUnitilise.add(port);
        portUtilise.remove(port);
    }

    /**
     * Permet de supprimer un Contact dans la liste des Contacts
     * @param c: Contact
     */
    public void removeContact(Contact c) {

        lContacts.removeContact(c.getNickname());
        mChatGUI.removeContact(c);

    }

    /**
     * Fonction qui gère la réception d'un nouveau message texte.<br/>
     *      - envoie le message au niveau du GUI
     * @param m: String message
     * @param rCont: Contact: l'expéditeur
     */
    public void newMessage(String m, Contact rCont) {

        this.mChatGUI.refreshMsg(rCont.getNickname(), m);
    }

    /**
     * Fonction utilisée à la réception d'un nouveau fichier:<br/>
     *      - envoie un signal au ChatGUI pour notifier l'utilisateur<br/>
     *      - envoie un ACK (acquitement) au serveur si le transfert peut se faire ou pas<br/>
     * @param f : String nom du fichier
     * @param rCont: String: le nickname de l'expéditeur
     * @param port: int: le port ouvert au niveau du serveur
     * @param taille: int: la taille du fichier pour gérer le progressBar
     */
    public void newFile(String f, Contact rCont, int port, int taille) {

        /**
         * Création de ack contenant la source et le numéro de port.
         * Le port permet de retrouver le bon socket ouvert
         */
        FichierAck ack = new FichierAck(rCont, Boolean.FALSE, port);

        /* Permet de récupérer la réponde de l'utilisateur.<br/>
         * - s'il il a cliquer sur "Annuler" s est à nulle et le serveur ferme le Socket à la réception du ACK<br/>
         * - s'il a accepter et choisie le répertoire: la réception commence et on affiche le progressBar<br/>
         *
         */
        String s = this.mChatGUI.refreshFile(f);

        if (s.equalsIgnoreCase("nulle")) {
            ack.setAck(Boolean.FALSE);

        } else {

            ack.setAck(Boolean.TRUE);

            ReceptionFile receptionFile = new ReceptionFile(rCont.getAddress(), port, s, taille);
            mChatGUI.addProgress(receptionFile.getProgressBar(), rCont.getNickname(), lUser.getNickname(), f);

        }

        mChatNI.sendAck(ack);
    }

    /**
     *Permet de faire un lien entre les classes ChatGUI et ChatController
     * @param g : ChatGUI
     */
    public void setGUI(ChatGUI g) {
        this.mChatGUI = g;
    }

    /**
     *Permet de faire un lien entre les classes ChatNI et ChatController
     * @param n : ChatNI
     */
    public void setNI(ChatNI n) {
        this.mChatNI = n;
    }

    /**
     * Permet de faire un lien entre les classes:  ChatSystem et ChatController
     * @param cs: ChatSytem
     */
    public void setChatSystems(ChatSystem cs) {
        this.chatSystem = cs;
    }

    private ArrayList<Contact> compDestCont(ArrayList<Integer> iContact) {
        return null;
    }

    /**
     * Retourne un objet de type ListContacs contenant tous les Contacts
     * @return Listcontacts
     */
    protected ListContacts getListContacts() {
        return this.lContacts;
    }

    /**
     * Retourne l'adresse IP de la machine
     * @return: String localeIP
     * 
     */
    public String getLocalAddress() {

        String localeIP = new String();
        Enumeration<NetworkInterface> e = null;
        try {
            e = NetworkInterface.getNetworkInterfaces();
        } catch (SocketException ex) {
            Logger.getLogger(ChatController.class.getName()).log(Level.SEVERE, null, ex);
        }
        while (e.hasMoreElements()) {
            Enumeration<InetAddress> i = e.nextElement().getInetAddresses();
            while (i.hasMoreElements()) {
                InetAddress a = i.nextElement();
                if (a.isSiteLocalAddress()) {
                    localeIP = a.getHostAddress();
                }
            }
        }
        return localeIP;

    }
}

